package com.dxy.shardingjdbc.algorithm;

import com.alibaba.fastjson.JSON;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.CRC32;

/**
 * @Description 订单编号-用户id，多键复核分片算法
 * @Author dangxianyue
 * @Date 2023/2/6 18:07
 */
public class OrderSubTableComplexAlgorithm implements ComplexKeysShardingAlgorithm {

    //订单编号中用户id的位数
    private static final int USER_INFO_LEN = 3;

    //订单编号列名
    private static final String COLUMN_ORDER_SN = "order_sn";

    //用户id列名
    private static final String COLUMN_USER_ID = "user_id";


    /**
     * 复合分片算法
     * 根据订单id(orderId)和客户id(userId)后3位计算
     * 订单id 包含用户id 的后3位
     * 以用户id的后3位来确定是路由到那个表中
     * 1、目前处理 = 和 in 操作，其余的操作，比如 >、< 等不支持。
     *
     * @param availableTargetNames 所有的分片集 由于我这个算法是指定了分表算法，则这里是物理表名列表，t_order_0、t_order_1、t_order_2、t_order_3
     * @param shardingValue        分片键值
     * @return 返回物理表名称集合，如 t_order_1
     */
    @Override
    public Collection<String> doSharding(Collection availableTargetNames, ComplexKeysShardingValue shardingValue) {
        System.out.println("availableTargetNames: " + JSON.toJSONString(availableTargetNames) + "\nshardingValue: " + JSON.toJSONString(shardingValue));
        //availableTargetNames: ["t_order_0","t_order_1","t_order_2","t_order_3"]
        //根据orderSn查询时的参数: shardingValue: {"columnNameAndShardingValuesMap":{"order_sn":["SB230206619743989236"]},"logicTableName":"t_order"}
        //根据userId查询时的参数: shardingValue: {"columnNameAndShardingValuesMap":{"user_id":["1000236"]},"logicTableName":"t_order"}
        //根据orderSn和userId查询时的参数: shardingValue: {"columnNameAndShardingValuesMap":{"order_sn":["SC230206981574256236"],"user_id":[1000236]},"logicTableName":"t_order"}

        // 获取订单编号
        Collection orderSns = (Collection) shardingValue.getColumnNameAndShardingValuesMap().getOrDefault(COLUMN_ORDER_SN, new ArrayList<>(1));

        // 获取用户id
        Collection userIds = (Collection) shardingValue.getColumnNameAndShardingValuesMap().getOrDefault(COLUMN_USER_ID, new ArrayList<>(1));

        // 整合orderSn和userId
        List<String> ids = new ArrayList<>();
        orderSns.forEach(o -> ids.add(o.toString()));
        userIds.forEach(o -> ids.add(o.toString()));

        List<String> tables = ids.stream()
                //截取 订单号或客户id的后3位
                .map(id -> id.substring(id.length() - USER_INFO_LEN))

                //去重
                .distinct()

                //进行 CRC32 哈希，让结果更均衡
                .map(idSuffix -> {
                    CRC32 crc32 = new CRC32();
                    crc32.update(idSuffix.getBytes());
                    return crc32.getValue();
                })

                //对可用的表总数求余数，获取到真实的表的后缀
                .map(idSuffixHash -> idSuffixHash % availableTargetNames.size())

                //转换成String
                .map(String::valueOf)

                //构建物理表名
                .map(tableSuffix -> shardingValue.getLogicTableName() + "_" + tableSuffix)
                .collect(Collectors.toList());
        System.out.println("tables: " + JSON.toJSONString(tables));
        return tables;
    }


}
